package search;

import java.io.IOException;
import java.util.LinkedList;

import net.barashev.seclub.io.BufferManager;
import net.barashev.seclub.io.DataMarshaller;
import net.barashev.seclub.io.IteratingSlot;
import net.barashev.seclub.io.OutputSlot;
import net.barashev.seclub.io.file.FileManager;
import net.barashev.seclub.io.file.VFile;
import net.barashev.seclub.io.marshaller.IntegerMarshaller;

public class DiscBufferedIdWOList {

	private FileManager fileMgr;
	private BufferManager bufferMgr;
	private String name;
	private int maxMemorySize;
	private int idx;
	private boolean inMemory;
	private boolean readable;
	private IteratingSlot<?, ? extends DataMarshaller<?>> inputSlot;
	private OutputSlot<Integer, ? extends DataMarshaller<Integer>> outputSlot;
	private static final int bufferSize = 1024;
	private int currMemorySize;
	private LinkedList<IdWithOccurences> list = new LinkedList<IdWithOccurences>();
	private VFile<Integer> file;

	public DiscBufferedIdWOList(FileManager fileMgr, BufferManager bufferMgr,
			String name, boolean free, int maxMemorySize) throws IOException {
		this.fileMgr = fileMgr;
		this.bufferMgr = bufferMgr;
		this.name = name;
		this.maxMemorySize = maxMemorySize;
		idx = 0;
		inMemory = free;
		readable = !free;
		if (readable) {
			this.file=fileMgr.getFile(name);
			inputSlot = bufferMgr.allocateIteratingSlot(bufferSize, file);
			inputSlot.pull();
		}
	}

	public DiscBufferedIntegerList extractIds() throws IOException {
		this.makeReadable();
		DiscBufferedIntegerList res=new DiscBufferedIntegerList(fileMgr, bufferMgr, "("+name+")", true, maxMemorySize);
		while(this.hasNext()){
			res.writeNext(this.readNext().id);
		}
		return res;
	}

	public String getName() {
		return name;
	}

	public FileManager getFileMgr() {
		return fileMgr;
	}

	public BufferManager getBufferMgr() {
		return bufferMgr;
	}
	
	public boolean hasNext(){
		if(inMemory){
			return !list.isEmpty();
		}else return (idx<inputSlot.getBlockCount());
	}
	
	public void releaseAll(){
		if(inputSlot!=null){
			inputSlot.release();
		}
		if(outputSlot!=null){
			outputSlot.release();
		}
	}

	public void makeReadable() throws IOException {
		if (!inMemory&!readable) {
			while (list.size() > 0) {
				IdWithOccurences curr = list.removeFirst();
				outputSlot.append(curr.id);
				outputSlot.append(curr.Occurences.size()+1);
				if (curr.isInMetaData) {
					outputSlot.append(-1);
				}else{
					outputSlot.append(-1);
				}
				while(curr.Occurences.size()>0){
					outputSlot.append(curr.Occurences.removeFirst());
				}
			}
			outputSlot.flush();
			outputSlot.release();
			inputSlot = bufferMgr.allocateIteratingSlot(bufferSize, fileMgr
					.getFile(name));
			inputSlot.pull();
			currMemorySize=0;
		}
		readable = true;
		idx = 0;
	}

	public IdWithOccurences readNext() throws IOException {
		if (list.size() == 0) {
			while (currMemorySize < maxMemorySize
					& idx < inputSlot.getBlockCount()) {
				IdWithOccurences next = new IdWithOccurences();
				next.id = (Integer) inputSlot.getBlock(idx).get(0);
				idx++;
				if (idx == 1024) {
					idx = 0;
					inputSlot.pull();
				}
				int n = (Integer) inputSlot.getBlock(idx).get(0) - 1;
				idx++;
				if (idx == 1024) {
					idx = 0;
					inputSlot.pull();
				}
				next.isInMetaData = ((Integer) inputSlot.getBlock(idx).get(0) != 0);
				next.Occurences = new LinkedList<Integer>();
				for (int i = 0; i < n; i++) {
					idx++;
					if (idx == 1024) {
						idx = 0;
						inputSlot.pull();
					}
					next.Occurences.add((Integer) inputSlot.getBlock(idx)
							.get(0));
				}
				idx++;
				list.addLast(next);
				currMemorySize+=(n+2);
			}
			if (idx == inputSlot.getBlockCount()) {
				inputSlot.release();
				inMemory=true;
			}
		}
		IdWithOccurences res=list.removeFirst();
		currMemorySize-=(res.Occurences.size()+2);
		return res;
	}

	public void writeNext(IdWithOccurences toWrite) throws IOException {
		if (currMemorySize > maxMemorySize-1) {
			if (inMemory) {
				if(fileMgr.getFile(name)==null){
					file=fileMgr.createFile(name, new IntegerMarshaller());
				}else{
					file=fileMgr.getFile(name);
				}
				outputSlot = bufferMgr.allocateOutputSlot(file);
				inMemory = false;
			}
			while (list.size() > 0) {
				IdWithOccurences curr = list.removeFirst();
				outputSlot.append(curr.id);
				outputSlot.append(curr.Occurences.size()+1);
				if (curr.isInMetaData) {
					outputSlot.append(-1);
				}else{
					outputSlot.append(0);
				}
				while(curr.Occurences.size()>0){
					outputSlot.append(curr.Occurences.removeFirst());
				}
			}
			currMemorySize=toWrite.Occurences.size()+2;
		}else{
			currMemorySize+=(toWrite.Occurences.size()+2);
		}
		list.addLast(toWrite);
	}
}
